package com.shd.day06;

import com.shd.day03.stream.Dish;

import java.util.*;

import static java.util.stream.Collectors.*;

/**
 * 分组
 * Created by shanhaodong on 2018/7/30
 */
public class GroupOperation {

    public static void main(String[] args) {
        // have a dish list
        List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH)
        );
        // 菜谱按照Type分组(Function 分类函数)
        Map<Dish.Type, List<Dish>> dishByType = menu.stream().collect(groupingBy(Dish::getType));
        System.out.println(dishByType);

        // 自定义分级
        Map<CaloricLevel, List<Dish>> collect = menu.stream().collect(groupingBy(dish -> getCaloricLevel(dish)));
        System.out.println(collect);

        // 多级分组(二级)(Type{热量[xxx]})
        Map<Dish.Type, Map<CaloricLevel, List<Dish>>> collect1 = menu.stream()
                .collect(groupingBy(Dish::getType, groupingBy(GroupOperation::getCaloricLevel)));
        System.out.println(collect1);

        // 一数菜单中每类菜有多少个
        Map<Dish.Type, Long> collect2 = menu.stream().collect(groupingBy(Dish::getType, counting()));
        System.out.println(collect2);

        // Tips: groupingBy(f) 单参数方法是 groupingBy(f,toList())的简写
        // 查找菜单中热量最高的菜肴的收集器改一改，按照菜分类
        Map<Dish.Type, Optional<Dish>> collect3 = menu.stream().collect(groupingBy(Dish::getType,
                maxBy(Comparator.comparingInt(Dish::getCalories))));
        System.out.println(collect3);

        // 去掉包装(Optional)
        Map<Dish.Type, Dish> collect4 = menu.stream().collect(groupingBy(Dish::getType,
                collectingAndThen(maxBy(Comparator.comparingInt(Dish::getCalories)), Optional::get)));

        // 和mapping 方法配合使用
        // 查出菜单中有多少热量等级
        Map<Dish.Type, Set<CaloricLevel>> collect5 = menu.stream().collect(groupingBy(Dish::getType, mapping(
                dish -> getCaloricLevel(dish), toSet())));
        System.out.println(collect5);
    }

    private static CaloricLevel getCaloricLevel(Dish dish) {
        if (dish.getCalories() <= 400) {
            return CaloricLevel.低热量;
        } else if (dish.getCalories() <= 700) {
            return CaloricLevel.中热量;
        } else {
            return CaloricLevel.高热量;
        }
    }


    enum CaloricLevel {低热量, 中热量, 高热量}
}
